/**
 * Sea.js 3.0.0 | seajs.org/LICENSE.md
 */
(function(global, undefined) {
  // Avoid conflicting when `sea.js` is loaded multiple times
  if (global.seajs) {
    return;
  }

  var seajs = (global.seajs = {
    // The current version of Sea.js being used
    version: "3.0.0"
  });

  var data = (seajs.data = {});

  /**
   * util-lang.js - The minimal language enhancement
   */

  function isType(type) {
    return function(obj) {
      return {}.toString.call(obj) == "[object " + type + "]";
    };
  }

  var isObject = isType("Object");
  var isString = isType("String");
  var isArray = Array.isArray || isType("Array");
  var isFunction = isType("Function");

  var _cid = 0;
  function cid() {
    return _cid++;
  }

  /**
   * util-events.js - The minimal events support
   */

  var events = (data.events = {});

  // Bind event
  seajs.on = function(name, callback) {
    var list = events[name] || (events[name] = []);
    list.push(callback);
    return seajs;
  };

  // Remove event. If `callback` is undefined, remove all callbacks for the
  // event. If `event` and `callback` are both undefined, remove all callbacks
  // for all events
  seajs.off = function(name, callback) {
    // Remove *all* events
    if (!(name || callback)) {
      events = data.events = {};
      return seajs;
    }

    var list = events[name];
    if (list) {
      if (callback) {
        for (var i = list.length - 1; i >= 0; i--) {
          if (list[i] === callback) {
            list.splice(i, 1);
          }
        }
      } else {
        delete events[name];
      }
    }

    return seajs;
  };

  // Emit event, firing all bound callbacks. Callbacks receive the same
  // arguments as `emit` does, apart from the event name
  var emit = (seajs.emit = function(name, data) {
    var list = events[name];

    if (list) {
      // Copy callback lists to prevent modification
      list = list.slice();

      // Execute event callbacks, use index because it's the faster.
      for (var i = 0, len = list.length; i < len; i++) {
        list[i](data);
      }
    }

    return seajs;
  });

  /**
   * util-path.js - The utilities for operating path such as id, uri
   */

  var DIRNAME_RE = /[^?#]*\//;

  var DOT_RE = /\/\.\//g;
  var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\//;
  var MULTI_SLASH_RE = /([^:/])\/+\//g;

  // Extract the directory portion of a path
  // dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"
  // ref: http://jsperf.com/regex-vs-split/2
  function dirname(path) {
    return path.match(DIRNAME_RE)[0];
  }

  // Canonicalize a path
  // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"
  function realpath(path) {
    // /a/b/./c/./d ==> /a/b/c/d
    path = path.replace(DOT_RE, "/");

    /*
    @author wh1100717
    a//b/c ==> a/b/c
    a///b/////c ==> a/b/c
    DOUBLE_DOT_RE matches a/b/c//../d path correctly only if replace // with / first
  */
    path = path.replace(MULTI_SLASH_RE, "$1/");

    // a/b/c/../../d  ==>  a/b/../d  ==>  a/d
    while (path.match(DOUBLE_DOT_RE)) {
      path = path.replace(DOUBLE_DOT_RE, "/");
    }

    return path;
  }

  // Normalize an id
  // normalize("path/to/a") ==> "path/to/a.js"
  // NOTICE: substring is faster than negative slice and RegExp
  function normalize(path) {
    var last = path.length - 1;
    var lastC = path.charCodeAt(last);

    // If the uri ends with `#`, just return it without '#'
    if (lastC === 35 /* "#" */) {
      return path.substring(0, last);
    }

    return path.substring(last - 2) === ".js" || path.indexOf("?") > 0 || lastC === 47 /* "/" */ ? path : path + ".js";
  }

  var PATHS_RE = /^([^/:]+)(\/.+)$/;
  var VARS_RE = /{([^{]+)}/g;

  function parseAlias(id) {
    var alias = data.alias;
    return alias && isString(alias[id]) ? alias[id] : id;
  }

  function parsePaths(id) {
    var paths = data.paths;
    var m;

    if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
      id = paths[m[1]] + m[2];
    }

    return id;
  }

  function parseVars(id) {
    var vars = data.vars;

    if (vars && id.indexOf("{") > -1) {
      id = id.replace(VARS_RE, function(m, key) {
        return isString(vars[key]) ? vars[key] : m;
      });
    }

    return id;
  }

  function parseMap(uri) {
    var map = data.map;
    var ret = uri;

    if (map) {
      for (var i = 0, len = map.length; i < len; i++) {
        var rule = map[i];

        ret = isFunction(rule) ? rule(uri) || uri : uri.replace(rule[0], rule[1]);

        // Only apply the first matched rule
        if (ret !== uri) break;
      }
    }

    return ret;
  }

  var ABSOLUTE_RE = /^\/\/.|:\//;
  var ROOT_DIR_RE = /^.*?\/\/.*?\//;

  function addBase(id, refUri) {
    var ret;
    var first = id.charCodeAt(0);

    // Absolute
    if (ABSOLUTE_RE.test(id)) {
      ret = id;
    }
    // Relative
    else if (first === 46 /* "." */) {
      ret = (refUri ? dirname(refUri) : data.cwd) + id;
    }
    // Root
    else if (first === 47 /* "/" */) {
      var m = data.cwd.match(ROOT_DIR_RE);
      ret = m ? m[0] + id.substring(1) : id;
    }
    // Top-level
    else {
      ret = data.base + id;
    }

    // Add default protocol when uri begins with "//"
    if (ret.indexOf("//") === 0) {
      ret = location.protocol + ret;
    }

    return realpath(ret);
  }

  function id2Uri(id, refUri) {
    if (!id) return "";

    id = parseAlias(id);
    id = parsePaths(id);
    id = parseAlias(id);
    id = parseVars(id);
    id = parseAlias(id);
    id = normalize(id);
    id = parseAlias(id);

    var uri = addBase(id, refUri);
    uri = parseAlias(uri);
    uri = parseMap(uri);

    return uri;
  }

  // For Developers
  seajs.resolve = id2Uri;

  // Check environment
  var isWebWorker = typeof window === "undefined" && typeof importScripts !== "undefined" && isFunction(importScripts);

  // Ignore about:xxx and blob:xxx
  var IGNORE_LOCATION_RE = /^(about|blob):/;
  var loaderDir;
  // Sea.js's full path
  var loaderPath;
  // Location is read-only from web worker, should be ok though
  var cwd = !location.href || IGNORE_LOCATION_RE.test(location.href) ? "" : dirname(location.href);

  if (isWebWorker) {
    // Web worker doesn't create DOM object when loading scripts
    // Get sea.js's path by stack trace.
    var stack;
    try {
      var up = new Error();
      throw up;
    } catch (e) {
      // IE won't set Error.stack until thrown
      stack = e.stack.split("\n");
    }
    // First line is 'Error'
    stack.shift();

    var m;
    // Try match `url:row:col` from stack trace line. Known formats:
    // Chrome:  '    at http://localhost:8000/script/sea-worker-debug.js:294:25'
    // FireFox: '@http://localhost:8000/script/sea-worker-debug.js:1082:1'
    // IE11:    '   at Anonymous function (http://localhost:8000/script/sea-worker-debug.js:295:5)'
    // Don't care about older browsers since web worker is an HTML5 feature
    var TRACE_RE = /.*?((?:http|https|file)(?::\/{2}[\w]+)(?:[\/|\.]?)(?:[^\s"]*)).*?/i;
    // Try match `url` (Note: in IE there will be a tailing ')')
    var URL_RE = /(.*?):\d+:\d+\)?$/;
    // Find url of from stack trace.
    // Cannot simply read the first one because sometimes we will get:
    // Error
    //  at Error (native) <- Here's your problem
    //  at http://localhost:8000/_site/dist/sea.js:2:4334 <- What we want
    //  at http://localhost:8000/_site/dist/sea.js:2:8386
    //  at http://localhost:8000/_site/tests/specs/web-worker/worker.js:3:1
    while (stack.length > 0) {
      var top = stack.shift();
      m = TRACE_RE.exec(top);
      if (m != null) {
        break;
      }
    }
    var url;
    if (m != null) {
      // Remove line number and column number
      // No need to check, can't be wrong at this point
      var url = URL_RE.exec(m[1])[1];
    }
    // Set
    loaderPath = url;
    // Set loaderDir
    loaderDir = dirname(url || cwd);
    // This happens with inline worker.
    // When entrance script's location.href is a blob url,
    // cwd will not be available.
    // Fall back to loaderDir.
    if (cwd === "") {
      cwd = loaderDir;
    }
  } else {
    var doc = document;
    var scripts = doc.scripts;

    // Recommend to add `seajsnode` id for the `sea.js` script element
    var loaderScript = doc.getElementById("seajsnode") || scripts[scripts.length - 1];

    function getScriptAbsoluteSrc(node) {
      return node.hasAttribute // non-IE6/7
        ? node.src
        : // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
          node.getAttribute("src", 4);
    }
    loaderPath = getScriptAbsoluteSrc(loaderScript);
    // When `sea.js` is inline, set loaderDir to current working directory
    loaderDir = dirname(loaderPath || cwd);
  }

  /**
   * util-request.js - The utilities for requesting script and style files
   * ref: tests/research/load-js-css/test.html
   */
  if (isWebWorker) {
    function requestFromWebWorker(url, callback, charset) {
      // Load with importScripts
      var error;
      try {
        importScripts(url);
      } catch (e) {
        error = e;
      }
      callback(error);
    }
    // For Developers
    seajs.request = requestFromWebWorker;
  } else {
    var doc = document;
    var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement;
    var baseElement = head.getElementsByTagName("base")[0];

    var currentlyAddingScript;

    function request(url, callback, charset) {
      var node = doc.createElement("script");

      if (charset) {
        var cs = isFunction(charset) ? charset(url) : charset;
        if (cs) {
          node.charset = cs;
        }
      }

      addOnload(node, callback, url);

      node.async = true;
      node.src = url;

      // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
      // the end of the insert execution, so use `currentlyAddingScript` to
      // hold current node, for deriving url in `define` call
      currentlyAddingScript = node;

      // ref: #185 & http://dev.jquery.com/ticket/2709
      baseElement ? head.insertBefore(node, baseElement) : head.appendChild(node);

      currentlyAddingScript = null;
    }

    function addOnload(node, callback, url) {
      var supportOnload = "onload" in node;

      if (supportOnload) {
        node.onload = onload;
        node.onerror = function() {
          emit("error", { uri: url, node: node });
          onload(true);
        };
      } else {
        node.onreadystatechange = function() {
          if (/loaded|complete/.test(node.readyState)) {
            onload();
          }
        };
      }

      function onload(error) {
        // Ensure only run once and handle memory leak in IE
        node.onload = node.onerror = node.onreadystatechange = null;

        // Remove the script to reduce memory leak
        if (!data.debug) {
          head.removeChild(node);
        }

        // Dereference the node
        node = null;

        callback(error);
      }
    }

    // For Developers
    seajs.request = request;
  }
  var interactiveScript;

  function getCurrentScript() {
    if (currentlyAddingScript) {
      return currentlyAddingScript;
    }

    // For IE6-9 browsers, the script onload event may not fire right
    // after the script is evaluated. Kris Zyp found that it
    // could query the script nodes and the one that is in "interactive"
    // mode indicates the current script
    // ref: http://goo.gl/JHfFW
    if (interactiveScript && interactiveScript.readyState === "interactive") {
      return interactiveScript;
    }

    var scripts = head.getElementsByTagName("script");

    for (var i = scripts.length - 1; i >= 0; i--) {
      var script = scripts[i];
      if (script.readyState === "interactive") {
        interactiveScript = script;
        return interactiveScript;
      }
    }
  }

  /**
   * util-deps.js - The parser for dependencies
   * ref: tests/research/parse-dependencies/test.html
   * ref: https://github.com/seajs/searequire
   */

  function parseDependencies(s) {
    if (s.indexOf("require") == -1) {
      return [];
    }
    var index = 0,
      peek,
      length = s.length,
      isReg = 1,
      modName = 0,
      parentheseState = 0,
      parentheseStack = [],
      res = [];
    while (index < length) {
      readch();
      if (isBlank()) {
      } else if (isQuote()) {
        dealQuote();
        isReg = 1;
      } else if (peek == "/") {
        readch();
        if (peek == "/") {
          index = s.indexOf("\n", index);
          if (index == -1) {
            index = s.length;
          }
        } else if (peek == "*") {
          index = s.indexOf("*/", index);
          if (index == -1) {
            index = length;
          } else {
            index += 2;
          }
        } else if (isReg) {
          dealReg();
          isReg = 0;
        } else {
          index--;
          isReg = 1;
        }
      } else if (isWord()) {
        dealWord();
      } else if (isNumber()) {
        dealNumber();
      } else if (peek == "(") {
        parentheseStack.push(parentheseState);
        isReg = 1;
      } else if (peek == ")") {
        isReg = parentheseStack.pop();
      } else {
        isReg = peek != "]";
        modName = 0;
      }
    }
    return res;
    function readch() {
      peek = s.charAt(index++);
    }
    function isBlank() {
      return /\s/.test(peek);
    }
    function isQuote() {
      return peek == '"' || peek == "'";
    }
    function dealQuote() {
      var start = index;
      var c = peek;
      var end = s.indexOf(c, start);
      if (end == -1) {
        index = length;
      } else if (s.charAt(end - 1) != "\\") {
        index = end + 1;
      } else {
        while (index < length) {
          readch();
          if (peek == "\\") {
            index++;
          } else if (peek == c) {
            break;
          }
        }
      }
      if (modName) {
        res.push(s.slice(start, index - 1));
        modName = 0;
      }
    }
    function dealReg() {
      index--;
      while (index < length) {
        readch();
        if (peek == "\\") {
          index++;
        } else if (peek == "/") {
          break;
        } else if (peek == "[") {
          while (index < length) {
            readch();
            if (peek == "\\") {
              index++;
            } else if (peek == "]") {
              break;
            }
          }
        }
      }
    }
    function isWord() {
      return /[a-z_$]/i.test(peek);
    }
    function dealWord() {
      var s2 = s.slice(index - 1);
      var r = /^[\w$]+/.exec(s2)[0];
      parentheseState = {
        if: 1,
        for: 1,
        while: 1,
        with: 1
      }[r];
      isReg = {
        break: 1,
        case: 1,
        continue: 1,
        debugger: 1,
        delete: 1,
        do: 1,
        else: 1,
        false: 1,
        if: 1,
        in: 1,
        instanceof: 1,
        return: 1,
        typeof: 1,
        void: 1
      }[r];
      modName = /^require\s*\(\s*(['"]).+?\1\s*\)/.test(s2);
      if (modName) {
        r = /^require\s*\(\s*['"]/.exec(s2)[0];
        index += r.length - 2;
      } else {
        index += /^[\w$]+(?:\s*\.\s*[\w$]+)*/.exec(s2)[0].length - 1;
      }
    }
    function isNumber() {
      return /\d/.test(peek) || (peek == "." && /\d/.test(s.charAt(index)));
    }
    function dealNumber() {
      var s2 = s.slice(index - 1);
      var r;
      if (peek == ".") {
        r = /^\.\d+(?:E[+-]?\d*)?\s*/i.exec(s2)[0];
      } else if (/^0x[\da-f]*/i.test(s2)) {
        r = /^0x[\da-f]*\s*/i.exec(s2)[0];
      } else {
        r = /^\d+\.?\d*(?:E[+-]?\d*)?\s*/i.exec(s2)[0];
      }
      index += r.length - 1;
      isReg = 0;
    }
  }
  /**
   * module.js - The core of module loader
   */

  var cachedMods = (seajs.cache = {});
  var anonymousMeta;

  var fetchingList = {};
  var fetchedList = {};
  var callbackList = {};

  var STATUS = (Module.STATUS = {
    // 1 - The `module.uri` is being fetched
    FETCHING: 1,
    // 2 - The meta data has been saved to cachedMods
    SAVED: 2,
    // 3 - The `module.dependencies` are being loaded
    LOADING: 3,
    // 4 - The module are ready to execute
    LOADED: 4,
    // 5 - The module is being executed
    EXECUTING: 5,
    // 6 - The `module.exports` is available
    EXECUTED: 6,
    // 7 - 404
    ERROR: 7
  });

  function Module(uri, deps) {
    this.uri = uri;
    this.dependencies = deps || [];
    this.deps = {}; // Ref the dependence modules
    this.status = 0;

    this._entry = [];
  }

  // Resolve module.dependencies
  Module.prototype.resolve = function() {
    var mod = this;
    var ids = mod.dependencies;
    var uris = [];

    for (var i = 0, len = ids.length; i < len; i++) {
      uris[i] = Module.resolve(ids[i], mod.uri);
    }
    return uris;
  };

  Module.prototype.pass = function() {
    var mod = this;

    var len = mod.dependencies.length;

    for (var i = 0; i < mod._entry.length; i++) {
      var entry = mod._entry[i];
      var count = 0;
      for (var j = 0; j < len; j++) {
        var m = mod.deps[mod.dependencies[j]];
        // If the module is unload and unused in the entry, pass entry to it
        if (m.status < STATUS.LOADED && !entry.history.hasOwnProperty(m.uri)) {
          entry.history[m.uri] = true;
          count++;
          m._entry.push(entry);
          if (m.status === STATUS.LOADING) {
            m.pass();
          }
        }
      }
      // If has passed the entry to it's dependencies, modify the entry's count and del it in the module
      if (count > 0) {
        entry.remain += count - 1;
        mod._entry.shift();
        i--;
      }
    }
  };

  // Load module.dependencies and fire onload when all done
  Module.prototype.load = function() {
    var mod = this;

    // If the module is being loaded, just wait it onload call
    if (mod.status >= STATUS.LOADING) {
      return;
    }

    mod.status = STATUS.LOADING;

    // Emit `load` event for plugins such as combo plugin
    var uris = mod.resolve();
    emit("load", uris);

    for (var i = 0, len = uris.length; i < len; i++) {
      mod.deps[mod.dependencies[i]] = Module.get(uris[i]);
    }

    // Pass entry to it's dependencies
    mod.pass();

    // If module has entries not be passed, call onload
    if (mod._entry.length) {
      mod.onload();
      return;
    }

    // Begin parallel loading
    var requestCache = {};
    var m;

    for (i = 0; i < len; i++) {
      m = cachedMods[uris[i]];

      if (m.status < STATUS.FETCHING) {
        m.fetch(requestCache);
      } else if (m.status === STATUS.SAVED) {
        m.load();
      }
    }

    // Send all requests at last to avoid cache bug in IE6-9. Issues#808
    for (var requestUri in requestCache) {
      if (requestCache.hasOwnProperty(requestUri)) {
        requestCache[requestUri]();
      }
    }
  };

  // Call this method when module is loaded
  Module.prototype.onload = function() {
    var mod = this;
    mod.status = STATUS.LOADED;

    // When sometimes cached in IE, exec will occur before onload, make sure len is an number
    for (var i = 0, len = (mod._entry || []).length; i < len; i++) {
      var entry = mod._entry[i];
      if (--entry.remain === 0) {
        entry.callback();
      }
    }

    delete mod._entry;
  };

  // Call this method when module is 404
  Module.prototype.error = function() {
    var mod = this;
    mod.onload();
    mod.status = STATUS.ERROR;
  };

  // Execute a module
  Module.prototype.exec = function() {
    var mod = this;

    // When module is executed, DO NOT execute it again. When module
    // is being executed, just return `module.exports` too, for avoiding
    // circularly calling
    if (mod.status >= STATUS.EXECUTING) {
      return mod.exports;
    }

    mod.status = STATUS.EXECUTING;

    if (mod._entry && !mod._entry.length) {
      delete mod._entry;
    }

    //non-cmd module has no property factory and exports
    if (!mod.hasOwnProperty("factory")) {
      mod.non = true;
      return;
    }

    // Create require
    var uri = mod.uri;

    function require(id) {
      var m = mod.deps[id] || Module.get(require.resolve(id));
      if (m.status == STATUS.ERROR) {
        throw new Error("module was broken: " + m.uri);
      }
      return m.exec();
    }

    require.resolve = function(id) {
      return Module.resolve(id, uri);
    };

    require.async = function(ids, callback) {
      Module.use(ids, callback, uri + "_async_" + cid());
      return require;
    };

    // Exec factory
    var factory = mod.factory;

    var exports = isFunction(factory) ? factory(require, (mod.exports = {}), mod) : factory;

    if (exports === undefined) {
      exports = mod.exports;
    }

    // Reduce memory leak
    delete mod.factory;

    mod.exports = exports;
    mod.status = STATUS.EXECUTED;

    // Emit `exec` event
    emit("exec", mod);

    return mod.exports;
  };

  // Fetch a module
  Module.prototype.fetch = function(requestCache) {
    var mod = this;
    var uri = mod.uri;

    mod.status = STATUS.FETCHING;

    // Emit `fetch` event for plugins such as combo plugin
    var emitData = { uri: uri };
    emit("fetch", emitData);
    var requestUri = emitData.requestUri || uri;

    // Empty uri or a non-CMD module
    if (!requestUri || fetchedList.hasOwnProperty(requestUri)) {
      mod.load();
      return;
    }

    if (fetchingList.hasOwnProperty(requestUri)) {
      callbackList[requestUri].push(mod);
      return;
    }

    fetchingList[requestUri] = true;
    callbackList[requestUri] = [mod];

    // Emit `request` event for plugins such as text plugin
    emit(
      "request",
      (emitData = {
        uri: uri,
        requestUri: requestUri,
        onRequest: onRequest,
        charset: isFunction(data.charset) ? data.charset(requestUri) || "utf-8" : data.charset
      })
    );

    if (!emitData.requested) {
      requestCache ? (requestCache[emitData.requestUri] = sendRequest) : sendRequest();
    }

    function sendRequest() {
      seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset);
    }

    function onRequest(error) {
      delete fetchingList[requestUri];
      fetchedList[requestUri] = true;

      // Save meta data of anonymous module
      if (anonymousMeta) {
        Module.save(uri, anonymousMeta);
        anonymousMeta = null;
      }

      // Call callbacks
      var m,
        mods = callbackList[requestUri];
      delete callbackList[requestUri];
      while ((m = mods.shift())) {
        // When 404 occurs, the params error will be true
        if (error === true) {
          m.error();
        } else {
          m.load();
        }
      }
    }
  };

  // Resolve id to uri
  Module.resolve = function(id, refUri) {
    // Emit `resolve` event for plugins such as text plugin
    var emitData = { id: id, refUri: refUri };
    emit("resolve", emitData);

    return emitData.uri || seajs.resolve(emitData.id, refUri);
  };

  // Define a module
  Module.define = function(id, deps, factory) {
    var argsLen = arguments.length;

    // define(factory)
    if (argsLen === 1) {
      factory = id;
      id = undefined;
    } else if (argsLen === 2) {
      factory = deps;

      // define(deps, factory)
      if (isArray(id)) {
        deps = id;
        id = undefined;
      }
      // define(id, factory)
      else {
        deps = undefined;
      }
    }

    // Parse dependencies according to the module factory code
    if (!isArray(deps) && isFunction(factory)) {
      deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString());
    }

    var meta = {
      id: id,
      uri: Module.resolve(id),
      deps: deps,
      factory: factory
    };

    // Try to derive uri in IE6-9 for anonymous modules
    if (!isWebWorker && !meta.uri && doc.attachEvent && typeof getCurrentScript !== "undefined") {
      var script = getCurrentScript();

      if (script) {
        meta.uri = script.src;
      }

      // NOTE: If the id-deriving methods above is failed, then falls back
      // to use onload event to get the uri
    }

    // Emit `define` event, used in nocache plugin, seajs node version etc
    emit("define", meta);

    meta.uri
      ? Module.save(meta.uri, meta)
      : // Save information for "saving" work in the script onload event
        (anonymousMeta = meta);
  };

  // Save meta data to cachedMods
  Module.save = function(uri, meta) {
    var mod = Module.get(uri);

    // Do NOT override already saved modules
    if (mod.status < STATUS.SAVED) {
      mod.id = meta.id || uri;
      mod.dependencies = meta.deps || [];
      mod.factory = meta.factory;
      mod.status = STATUS.SAVED;

      emit("save", mod);
    }
  };

  // Get an existed module or create a new one
  Module.get = function(uri, deps) {
    return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps));
  };

  // Use function is equal to load a anonymous module
  Module.use = function(ids, callback, uri) {
    var mod = Module.get(uri, isArray(ids) ? ids : [ids]);

    mod._entry.push(mod);
    mod.history = {};
    mod.remain = 1;

    mod.callback = function() {
      var exports = [];
      var uris = mod.resolve();

      for (var i = 0, len = uris.length; i < len; i++) {
        exports[i] = cachedMods[uris[i]].exec();
      }

      if (callback) {
        callback.apply(global, exports);
      }

      delete mod.callback;
      delete mod.history;
      delete mod.remain;
      delete mod._entry;
    };

    mod.load();
  };

  // Public API

  seajs.use = function(ids, callback) {
    Module.use(ids, callback, data.cwd + "_use_" + cid());
    return seajs;
  };

  Module.define.cmd = {};
  global.define = Module.define;

  // For Developers

  seajs.Module = Module;
  data.fetchedList = fetchedList;
  data.cid = cid;

  seajs.require = function(id) {
    var mod = Module.get(Module.resolve(id));
    if (mod.status < STATUS.EXECUTING) {
      mod.onload();
      mod.exec();
    }
    return mod.exports;
  };

  /**
   * config.js - The configuration for the loader
   */

  // The root path to use for id2uri parsing
  data.base = loaderDir;

  // The loader directory
  data.dir = loaderDir;

  // The loader's full path
  data.loader = loaderPath;

  // The current working directory
  data.cwd = cwd;

  // The charset for requesting files
  data.charset = "utf-8";

  // data.alias - An object containing shorthands of module id
  // data.paths - An object containing path shorthands in module id
  // data.vars - The {xxx} variables in module id
  // data.map - An array containing rules to map module uri
  // data.debug - Debug mode. The default value is false

  seajs.config = function(configData) {
    for (var key in configData) {
      var curr = configData[key];
      var prev = data[key];

      // Merge object config such as alias, vars
      if (prev && isObject(prev)) {
        for (var k in curr) {
          prev[k] = curr[k];
        }
      } else {
        // Concat array config such as map
        if (isArray(prev)) {
          curr = prev.concat(curr);
        }
        // Make sure that `data.base` is an absolute path
        else if (key === "base") {
          // Make sure end with "/"
          if (curr.slice(-1) !== "/") {
            curr += "/";
          }
          curr = addBase(curr);
        }

        // Set config
        data[key] = curr;
      }
    }

    emit("config", configData);
    return seajs;
  };
})(this);
